
#include "Log.hpp"
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>  //
#include <sys/socket.h> //
#include <netinet/in.h> //  !  这两货从哪来 tip:inet_addr
#include <arpa/inet.h>  //  !
#include <cassert>
#include <unistd.h>
#include <ctype.h>
#include <pthread.h>

#define BUFFER_SIZE 1024

class UdpServer; //先声明

static void Usage(const std::string porc)
{
    std::cout << "Usage:\n\t" << porc << " port [ip]" << std::endl;
}

class ThreadData
{
public:
    uint16_t clientPort_;
    std::string clinetIp_;
    int sock_;
    UdpServer *this_;

public:
    ThreadData(uint16_t port, std::string ip, int sock, UdpServer *ts)
        : clientPort_(port), clinetIp_(ip), sock_(sock), this_(ts)
    {
    }
};

// ./UdeServer port [ip]
class UdpServer
{
public:
    UdpServer(int port, std::string ip = "")
        : _port((uint16_t)port),
          _ip(ip),
          _socket(-1)
    {
    }
    ~UdpServer()
    {
    }

public:
    //初始化服务器
    void init()
    {
        // 1. 创建 soket 套接字 : domain type protocol
        _socket = socket(AF_INET, SOCK_DGRAM, 0);
        if (_socket < 0)
        {
            std::cerr << __LINE__ << "：socket err" << std::endl;
            exit(1);
        }
        // 2. 绑定网络信息,指名ip+port
        // 2.1首先填充基本信息到 struct sockaddr_in
        struct sockaddr_in local;
        bzero(&local, 0);
        //填充协议家族 域(网络通信还是本地通信)
        local.sin_family = AF_INET; // PF_INET一样的 本地通信还是网络通信
        //填充服务器对应的端口号信息，一定是会发送给对方的，_port一定会到网络中,所以htons
        local.sin_port = htons(_port);
        //服务器都有ip地址，"15，23，45，888"，字符串风格的点分十进制 -> 因为每小段都是0-255所以每小段都是一个字节就可以了
        //所以要转化为 四字节ip -> uint32_t ip
        // INADDR_ANY(本质是0): 程序员不关心会bind到哪一个ip， 任意地址bind，强烈推荐的做法，所有服务器一般的做法
        // inet_addr: 指定填充确定的IP，特殊用途，或者测试时使用，除了做转化，还会自动给我们进行 h—>n
        // 云服务器有一些特殊情况：
        // 禁止你bind云服务器上的任何确定IP， 只能使用INADDR_ANY，如果你是虚拟机，随意
        local.sin_addr.s_addr = _ip.empty() ? htonl(INADDR_ANY) : inet_addr(_ip.c_str());
        // 2.2bind 网络信息 因为之前的local是在用户栈创建的，是临时变量，现在要把他写入内核中
        if (bind(_socket, (sockaddr *)&local, sizeof(local)) < 0)
        {
            std::cerr << __LINE__ << "bind err" << std::endl;
            exit(2);
        }
        std::cout << "socket bind success : " << _socket << std::endl;
        std::cout << "creat server success !" << std::endl;
    }

    // 大小写转化服务
    // TCP && UDP: 支持全双工
    void transService(std::string &inbuffer,int s)
    {
        std::cout << "into transService!" << std::endl;
        // char inbuffer[BUFFER_SIZE];
        std::cout << "Uper before:" << inbuffer << std::endl;
        for (int i = 0; i < s; i++)
        {
            if (isalpha(inbuffer[i]) && islower(inbuffer[i]))
                inbuffer[i] = toupper(inbuffer[i]);
        }
        std::cout << "Uper after:" << inbuffer << std::endl;
    }

    // static void *threadRoutine(void *args)
    // {
    //     std::cout << "into thread" << std::endl;
    //     pthread_detach(pthread_self()); //设置线程分离
    //     ThreadData *td = static_cast<ThreadData *>(args);
    //     // ThreadData* td=(ThreadData*)args;
    //     //然后是线程去执行服务
    //     td->this_->transService(td->sock_, td->clinetIp_, td->clientPort_);
    //     delete td;
    //     return nullptr;
    // }

    //启动服务器
    void start()
    {
        // 服务器设计的时候，服务器都是死循环
        std::cout << "开始启动服务器" << std::endl;
        std::string inbuffer;
        inbuffer.resize(1024);

        // char inbuffer[1024];
        // char outbuffer[1024];
        while (true)
        {
            struct sockaddr_in RmtPort; // RmtPort 远端
            socklen_t len = sizeof(RmtPort);
            //  UDP无连接的
            //  对方给你发了消息，你想不想给对方回消息？要的！后面的两个参数是输出型参数,代表谁给你发的
            ssize_t s = recvfrom(_socket, (void *)inbuffer.c_str(), sizeof(inbuffer) - 1, 0, //阻塞等待
                                 (struct sockaddr *)&RmtPort, &len);
            if (s > 0)
            {
                std::cout << "s>0" << std::endl;
                inbuffer[s] = '\0'; //当作字符串，因为上面-1了的就是给'\0'预留位置
            }
            else if (s == -1)
            {
                std::cout << __LINE__ << " : recvfrom warning" << std::endl;
                std::cout << strerror(errno) << std::endl; // pos
                continue;                                  //因为服务器不能退出
            }

            //到这就读取成功了
            std::string RmtPortIp = inet_ntoa(RmtPort.sin_addr);
            uint16_t RmtPortPort = RmtPort.sin_port;
            LogMessage(RmtPortIp, RmtPortPort, "server read client success");
            std::cout << "[ip:" << RmtPortIp << "][port:" << RmtPortPort << "]"
                      << ": " << inbuffer << std::endl;

            // //开始提供服务
            // ThreadData *td = new ThreadData(RmtPortPort, RmtPortIp, _socket, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, threadRoutine, (void *)td);
            // std::cout << "endl...." << std::endl;

            transService(inbuffer,s);

            sendto(_socket, inbuffer.c_str(), s, 0, (struct sockaddr *)&RmtPort, len);
            std::cout << "send to cliet[ip：" << RmtPortIp << "]"<< "[port：" << RmtPortPort << "]" << std::endl;
        }
    }

private:
    //服务器必须要有端口号信息
    uint16_t _port;
    //服务器必须要有ip地址
    std::string _ip;
    //服务器的 socket fd 信息
    int _socket;
};

int main(int argc, char *argv[])
{
    if (argc != 2 && argc != 3) //反面：argc == 2 || argc == 3
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    std::string ip;
    if (argc == 3)
    {
        ip = argv[2];
    }
    UdpServer udpser(port, ip);
    udpser.init();
    udpser.start();
    return 0;
}